package algo_annotations.heuristique;

import commons.params_appli.ParamsAppli;
import commons.utils.Utils;
import java.lang.reflect.Array;


public class Genome  implements Comparable<Genome> {

	final private int DX = 0;
	final private int DY = 1;
	final private int NOTE = 2;
	final private int ML = 3; // gene multiligne (optionnel selon params utilisateur)

	private int taille;
	private float[][] data;
	private boolean bUseML;
	
	private double noteGlobale;
	public double getNoteGlobale() {return noteGlobale;}
	public void setNoteGlobale(double note) {this.noteGlobale = note;}

	public int getTaille() {return taille;}

	public float getDX (int numGene) {return data[numGene][DX];}
	public float getDY (int numGene) {return data[numGene][DY];}
	public float getML (int numGene) {return data[numGene][ML];}
	
	public float getNote (int numGene) {return data[numGene][NOTE];}
	public void setNote (int numGene, double note) {data[numGene][NOTE] = (float)note;}

	public Genome(int taille, boolean bUseML) {
		this.taille = taille;
		this.bUseML = bUseML;

		if (bUseML)
			data = new float[taille][4];
		else
			data = new float[taille][3];

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				data[i][DX] = 0;
				data[i][DY] = 0;
				data[i][NOTE] = -1;
				data[i][ML] = 0;
			}
		else
			for (int i=0; i<taille ; i++) {
				data[i][DX] = 0;
				data[i][DY] = 0;
				data[i][NOTE] = -1;
			}

		noteGlobale=-1;
	}

	public int compareTo(Genome g) {
	    if (this.noteGlobale < g.noteGlobale) return -1;
	    if (this.noteGlobale == g.noteGlobale) return 0;
	    return 1;
	}

	public void resetNote() {
		for (int i=0; i<taille ; i++)
			data[i][NOTE] = -1;
		noteGlobale=-1;
	}

	// Récupère les gène d'un autre genome
	void becomeCopyOf(Genome g) {
		if (bUseML)
			for (int i=0; i<taille ; i++) {
				this.data[i][DX] = g.data[i][DX];
				this.data[i][DY] = g.data[i][DY];
				this.data[i][NOTE] = g.data[i][NOTE];
				this.data[i][ML] = g.data[i][ML];
			}
		else
			for (int i=0; i<taille ; i++) {
				this.data[i][DX] = g.data[i][DX];
				this.data[i][DY] = g.data[i][DY];
				this.data[i][NOTE] = g.data[i][NOTE];
			}

		this.noteGlobale = g.noteGlobale;
	}

	void setFullRandomGenes() {
		if (bUseML)
			for (int i=0; i<taille ; i++) {
				this.data[i][DX] = (float)Utils.rnd();
				this.data[i][DY] = (float)Utils.rnd();
				this.data[i][ML] = (float)Utils.rnd();
			}
		else
			for (int i=0; i<taille ; i++) {
				this.data[i][DX] = (float)Utils.rnd();
				this.data[i][DY] = (float)Utils.rnd();
			}
	}


	Genome becomeRandomMutate(Genome g, double probaMutate) {
		assert(probaMutate>=0 && probaMutate<=1);

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaMutate) {
					this.data[i][DX] = (float)Utils.rnd();
					this.data[i][DY] = (float)Utils.rnd();
					this.data[i][ML] = (float)Utils.rnd();
				} else {
					this.data[i][DX] = g.data[i][DX];
					this.data[i][DY] = g.data[i][DY];
					this.data[i][ML] = g.data[i][ML];
				}
			}
		else
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaMutate) {
					this.data[i][DX] = (float)Utils.rnd();
					this.data[i][DY] = (float)Utils.rnd();
				} else {
					this.data[i][DX] = g.data[i][DX];
					this.data[i][DY] = g.data[i][DY];
				}
			}
		return this;
	}

	void randomMutate(double probaMutate) {
		assert(probaMutate>=0 && probaMutate<=1);

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaMutate) {
					this.data[i][DX] = (float)Utils.rnd();
					this.data[i][DY] = (float)Utils.rnd();
					this.data[i][ML] = (float)Utils.rnd();
				}
			}
		else
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaMutate) {
					this.data[i][DX] = (float)Utils.rnd();
					this.data[i][DY] = (float)Utils.rnd();
				}
			}
	}

	//probaFstGenom = 0.5 => this deviendra un mix équilibré de g1 et g2
	//probaFstGenom = 0.9 => this deviendra quasiment égal a g1
	void becomeCrossOverOf(Genome g1, Genome g2, double probaFstGenom) {
		assert(probaFstGenom>=0 && probaFstGenom<=1);

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaFstGenom) {
					this.data[i][DX] = g1.data[i][DX];
					this.data[i][DY] = g1.data[i][DY];
					this.data[i][ML] = g1.data[i][ML];
				} else {
					this.data[i][DX] = g2.data[i][DX];
					this.data[i][DY] = g2.data[i][DY];
					this.data[i][ML] = g2.data[i][ML];
				}
			}
		else
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaFstGenom) {
					this.data[i][DX] = g1.data[i][DX];
					this.data[i][DY] = g1.data[i][DY];
				} else {
					this.data[i][DX] = g2.data[i][DX];
					this.data[i][DY] = g2.data[i][DY];
				}
			}
	}

	void takeBestFrom(Genome g1, Genome g2, double probaTake) {

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaTake) {
					if (g1.data[i][NOTE]<g2.data[i][NOTE]) {
						this.data[i][DX] = g1.data[i][DX];
						this.data[i][DY] = g1.data[i][DY];
						this.data[i][ML] = g1.data[i][ML];
					} else {
						this.data[i][DX] = g2.data[i][DX];
						this.data[i][DY] = g2.data[i][DY];
						this.data[i][ML] = g2.data[i][ML];
					}
				}
			}
		else
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaTake) {
					if (g1.data[i][NOTE]<g2.data[i][NOTE]) {
						this.data[i][DX] = g1.data[i][DX];
						this.data[i][DY] = g1.data[i][DY];
					} else {
						this.data[i][DX] = g2.data[i][DX];
						this.data[i][DY] = g2.data[i][DY];
					}
				}
			}
	}

	void becomeAdjustedOf(Genome g, double probaAdjust) {
		assert(probaAdjust>=0 && probaAdjust<=1);

		if (bUseML)
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaAdjust) {

					double new_dx = g.data[i][DX] + (Utils.rnd() - 0.5) * g.data[i][NOTE] * ParamsAppli.ANNO_MUTATION_FACTOR_ADJUST;
					if (new_dx >= 1) new_dx = 0.99999;
					if (new_dx < 0) new_dx = 0;

					double new_dy = g.data[i][DY] + (Utils.rnd() - 0.5) * g.data[i][NOTE] * ParamsAppli.ANNO_MUTATION_FACTOR_ADJUST;
					if (new_dy >= 1) new_dy = 0.99999;
					if (new_dy < 0) new_dy = 0;

					// TODO ALGO ? : prevoir des variation plus fortes ? cf. peu de valeurs possibles
					double new_ml = g.data[i][ML] + (Utils.rnd() - 0.5) * g.data[i][NOTE] * ParamsAppli.ANNO_MUTATION_FACTOR_ADJUST;
					if (new_ml >= 1) new_ml = 0.99999;
					if (new_ml < 0) new_ml = 0;

					this.data[i][DX] = (float)new_dx;
					this.data[i][DY] = (float)new_dy;
					this.data[i][ML] = (float)new_ml;
				}
			}
		else
			for (int i=0; i<taille ; i++) {
				if (Utils.rnd() <= probaAdjust) {

					double new_dx = g.data[i][DX] + (Utils.rnd() - 0.5) * g.data[i][NOTE] * ParamsAppli.ANNO_MUTATION_FACTOR_ADJUST;
					if (new_dx >= 1) new_dx = 0.99999;
					if (new_dx < 0) new_dx = 0;

					double new_dy = g.data[i][DY] + (Utils.rnd() - 0.5) * g.data[i][NOTE] * ParamsAppli.ANNO_MUTATION_FACTOR_ADJUST;
					if (new_dy >= 1) new_dy = 0.99999;
					if (new_dy < 0) new_dy = 0;

					this.data[i][DX] = (float)new_dx;
					this.data[i][DY] = (float)new_dy;
				}
			}

	}


}
